/*
 * Created by Jaren at 2021/11/28 10:16
 */
import 'dart:typed_data';

import 'package:binary/binary.dart';
import 'package:inmotion_protocol/util/byte_util.dart';

class Datetime {
  static const int length = 4;

  static const int yearStartAt = 2020;

  static final Datetime emptyDate = Datetime.create(Uint8.zero, Uint8.zero, Uint8.zero, Uint8.zero, Uint8.zero, Uint8.zero);

  Datetime();

  Uint8 _year = Uint8.zero; // 占6位，范围2020-2083, 0表示2020
  Uint8 month = Uint8.zero; // 占4位，范围1-12
  Uint8 day = Uint8.zero; // 占5位，范围1-31

  Uint8 hour = Uint8.zero; // 占5位，范围0-23
  Uint8 minute = Uint8.zero; // 占6位，范围0-59
  Uint8 second = Uint8.zero; // 占6位，范围0-59

  Datetime.create(this._year, this.month, this.day, this.hour, this.minute, this.second);

  Datetime.fromDateTime(DateTime dateTime) {
    if (dateTime.year == 1970) {
      _year = Uint8(0);
    } else {
      _year = Uint8(dateTime.year - yearStartAt);
    }
    month = Uint8(dateTime.month);
    day = Uint8(dateTime.day);
    hour = Uint8(dateTime.hour);
    minute = Uint8(dateTime.minute);
    second = Uint8(dateTime.second);
  }

  bool isEmpty() {
    return _year.value == 0 && month.value == 0 && day.value == 0 && hour.value == 0 && minute.value == 0 && second.value == 0;
  }

  get year {
    return _year.value + yearStartAt;
  }

  DateTime toDateTime() {
    if (isEmpty()) return DateTime(1970, 1, 1);
    return DateTime(year, month.value, day.value, hour.value, minute.value, second.value);
  }

  static bool isActivated(DateTime dateTime) {
    return !(dateTime.year == 1970 && dateTime.month == 1 && dateTime.day == 1 && dateTime.hour == 0 && dateTime.minute == 0 && dateTime.second == 0);
  }

  static Datetime fromBytes(Uint8ClampedList bytes, {int offset = 0, Datetime? output}) {
    output ??= Datetime();

    if (bytes.length < offset + length) {
      return output;
    }
    int dateInt = ByteUtil.bytes2Uint32Le(bytes, offset).value;
    output._year = ByteUtil.toUint8((dateInt & 0x3F));
    output.month = ByteUtil.toUint8((dateInt >> 6) & 0x0F);
    output.day = ByteUtil.toUint8((dateInt >> (6 + 4)) & 0x1F);
    output.hour = ByteUtil.toUint8((dateInt >> (6 + 4 + 5)) & 0x1F);
    output.minute = ByteUtil.toUint8((dateInt >> (6 + 4 + 5 + 5)) & 0x3F);
    output.second = ByteUtil.toUint8((dateInt >> (6 + 4 + 5 + 5 + 6)) & 0x3F);

    return output;
  }

  Uint8ClampedList toBytes() {
    int destYear = _year.value;
    int destMonth = month.value << 6;
    int destDay = day.value << (6 + 4);
    int destHour = hour.value << (6 + 4 + 5);
    int destMinute = minute.value << (6 + 4 + 5 + 5);
    int destSecond = second.value << (6 + 4 + 5 + 5 + 6);

    int date = destYear | destMonth | destDay | destHour | destMinute | destSecond;
    Uint8ClampedList bytes = Uint8ClampedList(4);
    ByteUtil.num2BytesLe(date, 4, bytes, 0);
    return bytes;
  }

  @override
  String toString() {
    return 'Datetime{$_year-${month.value}-${day.value} ${hour.value}:${minute.value}:${second.value}}';
  }
}

class Date {
  static const int length = 2;

  static const int yearStartAt = 2020;

  static final Date emptyDate = Date.create(Uint8.zero, Uint8.zero, Uint8.zero);

  Date();

  Uint8 _year = Uint8.zero; // 占7位，范围2020-2084, 0表示2020
  Uint8 month = Uint8.zero; // 占4位，范围1-12
  Uint8 day = Uint8.zero; // 占5位，范围1-31

  Date.create(this._year, this.month, this.day);

  bool isEmpty() {
    return _year.value == 0 && month.value == 0 && day.value == 0;
  }

  get year {
    return _year.value + yearStartAt;
  }

  DateTime toDateTime() {
    return DateTime(year, month.value, day.value, 0, 0, 0);
  }

  static Date fromBytes(Uint8ClampedList bytes, {int offset = 0, Date? output}) {
    output ??= Date();
    if (bytes.length < offset + length) {
      return output;
    }
    int dateInt = ByteUtil.bytes2Uint16Le(bytes, offset).value;
    output._year = ByteUtil.toUint8((dateInt & 0x7F));
    output.month = ByteUtil.toUint8((dateInt >> 7) & 0x0F);
    output.day = ByteUtil.toUint8((dateInt >> (7 + 4)) & 0x1F);
    return output;
  }

  @override
  String toString() {
    return 'Date{$year-${month.value}-${day.value}}';
  }
}
